import {
  Component, OnInit, ViewChild, TemplateRef, Output, EventEmitter, Renderer2, ElementRef, ChangeDetectorRef, Input, ViewChildren
} from '@angular/core';
import { NotifyService } from '@farris/ui-notify';
import { TreeNode, TreeTableComponent } from '@farris/ui-treetable';
import { cloneDeep, set } from 'lodash-es';
import { SchemaPropName } from './prop-name';
import { VariableContrastService } from './variable-contrast.service';
import { FormSchema, FormSchemaEntity, FormSchemaEntityField, DomService, DesignViewModelService, RefreshFormService } from '@farris/designer-services';
import { IRefreshAfterChangedService } from './refresh.service';
import { PopoverDirective } from '@farris/ui-popover';
import { FarrisTabComponent } from '@farris/ui-tabs';

/**
 * 更新表单Schema组件
 */
@Component({
  selector: 'app-update-schema',
  templateUrl: './update-schema.component.html',
  styleUrls: ['./update-schema.component.css'],
  providers: [VariableContrastService]
})
export class UpdateSchemaComponent implements OnInit {
  @Input() oldSchema: FormSchema;
  @Input() newSchema: FormSchema;
  @Input() refreshAfterChangedServ: IRefreshAfterChangedService;
  @Output() closeModal = new EventEmitter<any>();

  @ViewChild('btns') btns: TemplateRef<any>;
  @ViewChildren(PopoverDirective) popoverContainers: Array<PopoverDirective>;
  @ViewChild(FarrisTabComponent) tabCmp: FarrisTabComponent;

  /**  旧schema */
  oldSchemaEntities: FormSchemaEntity[];

  /** 新schema */
  newSchemaEntities: FormSchemaEntity[];

  /** 新增字段树表绑定数据 */
  addedTreeData: TreeNode[] = [];

  /** 删除字段树表绑定数据 */
  deletedTreeData: TreeNode[] = [];

  /** 选中的新增字段 */
  addSelected;

  /** 选中的删除字段 */
  deleteSelected;

  addedFields;

  /** 表格列配置 */
  treeCols = [{ field: 'name', title: '名称' }, { field: 'bindingField', title: '绑定字段' }, { field: 'select', title: '更新' }];

  /** 新增的表 */
  addedTable = [];

  /** 删除的表 */
  deletedTable = [];

  /** 新增字段--全选 */
  addFieldSelectedAll = true;

  /** 删除字段---全选 */
  deleteFieldSelectedAll = true;

  /** 变更字段左侧树列配置 */
  leftTreeCols = [{ field: 'name', title: '字段名称' }];

  /** 变更字段左侧树绑定数据 */
  changeLeftTreeData = [];

  /**  变更字段左侧树选中节点id */
  selectedTreeKey = '';

  /** 是否显示变更列表 */
  showChangeContrast = false;

  /**  变更集合 */
  changeContrast = {};

  /** 选中的变更集合 */
  changeSelected = {};

  /** 字段的全选 */
  selectedAll = {};

  /** 平铺的变更集合 */
  changeList = [];

  /** 全选变更checkbox */
  selectAllChange = false;

  /** 变更radio 分类 */
  updateRadioTypes = [{ value: false, text: '部分更新' }, { value: true, text: '全部更新' }];


  /** 变更字段类型和编辑器类型的字段集合，用于页面中提示用户进行全选操作 */
  changeFieldTypeEditorTypeList = [];

  /** 变更字段类型，但编辑器类型不变的字段集合，用于页面中提示用户进行全选操作 */
  changeFieldTypeList = [];

  /**  变更复杂字段类型的字段集合，用于页面中提示用户将会删除已有控件，需要手动添加 */
  changeComplexFieldTypeList = [];

  /** 变更树表列配置 */
  changedTreeCols = [
    { field: 'propName', title: '属性名称' },
    { field: 'propCode', title: '属性编号' },
    { field: 'oldValue', title: '当前值' },
    { field: 'newValue', title: '新值' },
    { field: 'select', title: '更新', width: 120 }
  ];


  /** schema变量结构 */
  variableList = [];

  /** 变量树表列配置 */
  varTreeCols = [
    { field: 'name', title: '变量名称' },
    { field: 'code', title: '变量编号' },
    { field: 'type', title: '变量类型' },
    { field: 'modifyType', title: '更新' }
  ];

  /** 变量树表绑定数据 */
  variableTreeList = [];

  /** 是否更新变量 */
  isUpdateVariable = false;


  constructor(
    private notifyService: NotifyService,
    private domService: DomService,
    private dgVMService: DesignViewModelService,
    private variableService: VariableContrastService,
    private refreshFormService: RefreshFormService,
    private el: ElementRef,
    private cd: ChangeDetectorRef) {
  }

  ngOnInit() {
    this.show(this.oldSchema, this.newSchema);
  }

  show(oldSchema: FormSchema, newSchema: FormSchema) {
    this.init(oldSchema, newSchema);
    this.recursiveOldSchema(this.oldSchemaEntities, this.newSchemaEntities);
    this.recursiveNewSchema(this.oldSchemaEntities, this.newSchemaEntities);

    // 变量
    this.variableList = newSchema.variables;
    this.variableTreeList = this.variableService.getVariableTree(newSchema.variables, oldSchema.variables);

    // 新增、删除全选按钮
    this.addFieldSelectedAll = Object.keys(this.addSelected).length > 0;
    this.deleteFieldSelectedAll = Object.keys(this.deleteSelected).length > 0;

  }

  init(oldSchema, newSchema) {
    this.addSelected = {};
    this.deleteSelected = {};
    this.addedTreeData = [];
    this.deletedTreeData = [];
    this.addedFields = {};

    this.oldSchemaEntities = cloneDeep(oldSchema.entities);
    this.newSchemaEntities = newSchema.entities;


    this.changeSelected = {};
    this.changeLeftTreeData = [];
    this.changeContrast = {};
    this.changeList = [];

    this.showChangeContrast = false;
    this.selectedTreeKey = '';

    this.addedTable = [];

    this.selectAllChange = false;
    this.selectedAll = {};
    this.changeFieldTypeEditorTypeList = [];
    this.changeComplexFieldTypeList = [];

    this.variableTreeList = [];
    this.isUpdateVariable = false;
    this.variableList = [];
  }


  /**
   * 获取已删除字段、变更字段
   * @param oldSchemaEntities
   * @param newSchemaEntities
   */
  private recursiveOldSchema(oldSchemaEntities: FormSchemaEntity[], newSchemaEntities: FormSchemaEntity[]) {
    if (!oldSchemaEntities || oldSchemaEntities.length === 0 || !newSchemaEntities) {
      return;
    }
    oldSchemaEntities.forEach(entity => {
      const newEntity = newSchemaEntities.find(e => e.id === entity.id);
      if (!newEntity) { // 删除表
        this.deletedTreeData.push({ data: entity, selectable: true, children: [], expanded: true });
        this.deletedTable.push(entity.id);
        this.deleteSelected[entity.id] = true;
        return;
      }
      const deletedFields = [], otherFields = [];
      entity.type.fields.forEach(field => {
        const newField = newEntity.type.fields.find(f => f.id === field.id);
        if (!newField) { // 已删除字段
          deletedFields.push({ data: field, selectable: true });
          this.deleteSelected[field.id] = true;
        } else {
          // 对比字段属性差异
          const { leftTree, deletedFields: deletedChildFields } = this.contrastObject(field, newField, field.id, '');
          if (leftTree) {
            otherFields.push(leftTree);
          }
          if (deletedChildFields.length > 0) {
            deletedFields.push(...deletedChildFields);
          }
        }
      });
      const { type, ...basicEntity } = entity;
      if (deletedFields.length > 0) {
        this.deletedTreeData.push({ data: basicEntity, selectable: false, children: deletedFields, expanded: true });
      }
      if (otherFields.length > 0) {
        this.changeLeftTreeData.push({ data: { ...basicEntity }, children: otherFields, expanded: true });
      }

      if (entity.type.entities && entity.type.entities.length > 0) {
        this.recursiveOldSchema(entity.type.entities, newEntity.type.entities);
      }
    });
  }

  /**
   * 获取新增字段
   * @param oldSchemaEntities
   * @param newSchemaEntities
   */
  private recursiveNewSchema(oldSchemaEntities: FormSchemaEntity[], newSchemaEntities: FormSchemaEntity[], parentEntityId: string = '') {
    if (!oldSchemaEntities || !newSchemaEntities || newSchemaEntities.length === 0) {
      return;
    }
    newSchemaEntities.forEach(entity => {
      const oldEntity = oldSchemaEntities.find(e => e.id === entity.id);
      if (!oldEntity) { // 新增表
        this.addSelected[entity.id] = true;
        this.addedTreeData.push({ data: entity, selectable: true, children: [], expanded: true });
        this.addedTable.push({ parentEntityId, entity });
        return;
      }
      this.addedFields[entity.id] = [];
      const added = this.recursiveField(entity.type.fields, oldEntity.type.fields, entity.id);

      const { type, ...basicEntity } = entity;
      if (added.length > 0) {
        this.addedTreeData.push({ data: basicEntity, selectable: false, children: added, expanded: true });
      }
      if (entity.type.entities && entity.type.entities.length > 0) {
        const oldChildEntities = oldEntity && oldEntity.type ? oldEntity.type.entities : [];
        this.recursiveNewSchema(oldChildEntities, entity.type.entities, entity.id);
      }
    });
  }

  private recursiveField(newFields: FormSchemaEntityField[], oldFields: FormSchemaEntityField[], parentId) {
    const added = [];
    newFields.forEach(newField => {
      if (!oldFields) {
        added.push({ data: newField, selectable: true });
        this.addSelected[newField.id] = true;
        this.addedFields[parentId].push(newField);
        return;
      }
      const oldField = oldFields.find(f => f.id === newField.id);
      if (!oldField) {
        added.push({ data: newField, selectable: true });
        this.addSelected[newField.id] = true;
        this.addedFields[parentId].push(newField);
        return;
      }

      // 字段类型相同，并且有下级字段：遍历下级字段
      if (oldField.type.name === newField.type.name && newField.type.fields) {
        this.addedFields[newField.id] = [];
        const childAddedFields = this.recursiveField(newField.type.fields, oldField.type.fields, newField.id);
        if (childAddedFields.length > 0) {
          added.push({ data: newField, selectable: false, children: childAddedFields, expanded: true });
        }
      }
    });

    return added;
  }

  /**
   * 切换左侧树节点
   */
  handleTreeSelection($event) {
    this.hidePopoverContainers();

    // 选中表名
    if (!$event.node.data.$type) {
      this.showChangeContrast = false;
      return;
    }
    // 选中字段
    this.showChangeContrast = true;
    const curId = $event.node.data.id;

    this.selectedTreeKey = curId;
  }

  /**
   * 【更新】tab页的字段全选
   */
  clickSelectAllCheckbox() {
    const changeList = this.changeContrast[this.selectedTreeKey];
    this.selectChangeItem(changeList, this.selectedAll[this.selectedTreeKey], this.selectedTreeKey);

  }

  private selectChangeItem(changeList, isSelect, selectedTreeKey) {
    changeList.forEach(change => {
      if (change.selectable) {
        this.changeSelected[selectedTreeKey][change.data.propPath] = isSelect;
      } else if (change.children && change.children.length > 0) {
        this.selectChangeItem(change.children, isSelect, selectedTreeKey);
      }
    });
  }
  /**
   * 【更新】tab页的全选
   */
  selectAllChanged() {
    Object.keys(this.changeContrast).forEach(treeKey => {
      this.selectedAll[treeKey] = this.selectAllChange;
      this.selectChangeItem(this.changeContrast[treeKey], this.selectAllChange, treeKey);
    });

    this.hidePopoverContainers();
  }

  /**
   * 取消
   */
  clickCancel() {
    this.closeModal.emit();
  }

  /**
   * 确定
   */
  clickOK() {
    const newSchema = this.domService.getSchemas();

    // 处理extendProperties 节点，直接覆盖
    newSchema.extendProperties = this.newSchema.extendProperties;

    if (!this.checkIsNeedUpdate()) {
      this.closeModal.emit();
      return;
    }

    this.oldSchemaEntities = this.handleChangeSet(this.oldSchemaEntities, this.newSchemaEntities);
    // 处理主对象变更的情况
    const addedT = this.addedTable.find(t => t.parentEntityId === '');
    if (addedT && this.addSelected[addedT.entity.id]) {
      this.oldSchemaEntities.push(addedT.entity);
    }

    newSchema.entities = this.oldSchemaEntities;

    // 处理变量同步
    if (this.isUpdateVariable) {
      newSchema.variables = this.variableList;
      this.domService.updateRemoteVariables(this.variableList);
    }
    // 刷新实体树
    this.refreshFormService.refreshSchamaTree.next();

    // 同步表单控件
    this.syncForm();

    // 刷新页面
    this.refreshFormService.refreshFormDesigner.next();

    this.notifyService.success('更新成功！');

    this.closeModal.emit();

  }

  /**
   *  对比属性差异
   * @param oldValue
   * @param newValue
   * @param fieldId
   * @param path
   */
  private contrastObject(oldValue, newValue, fieldId, path) {
    const diffTree = [], propPath = path ? path + '.' : '', childLeftTree = [];
    let deletedFields = [];
    // 遍历基础属性
    const basicDiff = this.contrastBasicProp(oldValue, newValue, fieldId, propPath);
    if (basicDiff && basicDiff.length > 0) {
      diffTree.push(...basicDiff);
    }

    // 新增属性
    Object.keys(newValue).forEach(propCode => {
      if (propCode === 'editor') {
        return;
      }
      if (!Object.keys(oldValue).includes(propCode)) {
        const data = {
          propPath: propPath + propCode,
          fieldId,
          propCode,
          propName: SchemaPropName.getName(propCode),
          newValue: newValue[propCode],
          oldValue: '无'
        };
        diffTree.push({ selectable: true, data });
        this.changeList.push(data);
      }
    });


    if (oldValue.type && newValue.type) {
      const oldType = oldValue.type; const newType = newValue.type;
      // 类型变化后将type和editor整体覆盖，不再向下对比差异
      if (oldType.$type !== newType.$type) {
        // 排除类型变化，但编辑器不变的场景：日期和日期时间类型对应的控件类型都是DateBox
        if (!(oldValue.editor && newValue.editor && oldValue.editor.$type === newValue.editor.$type)) {
          const type = {
            propPath: propPath + 'type', fieldId, propCode: 'type', propName: SchemaPropName.getName('type'),
            oldValue: oldType, newValue: newType
          };
          const editor = {
            propPath: propPath + 'editor',
            fieldId,
            propCode: 'editor',
            propName: SchemaPropName.getName('editor'),
            oldValue: oldValue.editor === undefined ? '无' : oldValue.editor,
            newValue: newValue.editor
          };
          diffTree.push({ selectable: true, data: type, isObject: true });
          diffTree.push({ selectable: true, data: editor, isObject: true });
          this.changeList.push(type);
          this.changeList.push(editor);

          this.changeContrast[oldValue.id] = diffTree;
          this.changeSelected[oldValue.id] = {};
          this.selectedAll[oldValue.id] = false;

          this.changeFieldTypeEditorTypeList.push(oldValue.id);
          return { leftTree: { data: { ...oldValue } }, deletedFields };
        }
        this.changeFieldTypeList.push(oldValue.id);

      } else if (oldType.name !== newType.name) {
        // 复杂类型字段，更换了引用的对象
        const complexTypeDiff = {
          propPath: propPath + 'type',
          propCode: 'type',
          propName: SchemaPropName.getName('type'),
          fieldId,
          oldValue: oldType,
          newValue: newType,
          oldValueShowName: oldType.displayName || oldType.name,
          newValueShowName: newType.displayName
        };
        diffTree.push({ selectable: true, data: complexTypeDiff, isObject: true });
        this.changeList.push(complexTypeDiff);
        this.changeContrast[oldValue.id] = diffTree;
        this.changeSelected[oldValue.id] = {};
        this.selectedAll[oldValue.id] = false;
        this.changeComplexFieldTypeList.push(oldValue.id);
        return { leftTree: { data: { ...oldValue } }, deletedFields };
      } else if (oldValue.multiLanguage !== newValue.multiLanguage) {
        // 类型不变，但编辑器需要改变---目前场景：修改多语属性，但要排除现有表单新增多语为false的场景
        if (!(oldValue.multiLanguage === undefined && newValue.multiLanguage === false)) {
          if (!this.compareJsonByString(oldType, newType)) {
            const type = {
              propPath: propPath + 'type', fieldId, propCode: 'type', propName: SchemaPropName.getName('type'),
              oldValue: oldType, newValue: newType
            };
            diffTree.push({ selectable: true, data: type, isObject: true });
            this.changeList.push(type);
          }

          const editor = {
            propPath: propPath + 'editor',
            fieldId,
            propCode: 'editor',
            propName: SchemaPropName.getName('editor'),
            oldValue: oldValue.editor,
            newValue: newValue.editor
          };
          diffTree.push({ selectable: true, data: editor, isObject: true });
          this.changeList.push(editor);

          this.changeContrast[oldValue.id] = diffTree;
          this.changeSelected[oldValue.id] = {};
          this.selectedAll[oldValue.id] = false;

          this.changeFieldTypeEditorTypeList.push(oldValue.id);
          return { leftTree: { data: { ...oldValue } }, deletedFields };
        }

      }

      const typeDiff = this.contrastBasicProp(oldType, newType, fieldId, propPath + 'type.');
      if (oldType.valueType && newType.valueType) {
        const valueTypeDiff = this.contrastBasicProp(oldType.valueType, newType.valueType, fieldId, propPath + 'type.valueType.');
        if (valueTypeDiff && valueTypeDiff.length > 0) {
          typeDiff.push({
            selectable: false, children: valueTypeDiff, expanded: true,
            data: {
              propPath: propPath + 'type.valueType', fieldId, propCode: 'valueType',
              propName: SchemaPropName.getName('valueType')
            }
          });
        }
      }
      if (oldType.enumValues && newType.enumValues) {
        if (JSON.stringify(oldType.enumValues) !== JSON.stringify(newType.enumValues)) {
          const data = {
            propPath: propPath + 'type.enumValues', fieldId, propCode: 'enumValues',
            propName: SchemaPropName.getName('enumValues'), oldValue: oldType.enumValues, newValue: newType.enumValues
          };
          typeDiff.push({ selectable: true, data, isObject: true });
          this.changeList.push(data);
        }
      }
      if (oldType.fields) {
        oldType.fields.forEach(oldEle => {
          const newEle = newType.fields.find(f => f.id === oldEle.id);
          if (!newEle) {
            // 已删除字段
            deletedFields.push({ data: oldEle, selectable: true });
            this.deleteSelected[oldEle.id] = true;
          } else {
            const { leftTree: eleLeftTree, deletedFields: deletedChildFields } = this.contrastObject(oldEle, newEle, oldEle.id, '');
            if (eleLeftTree) {
              childLeftTree.push(eleLeftTree);
            }
            if (deletedChildFields.length > 0) {
              deletedFields.push(...deletedChildFields);
            }
          }
        });
      }
      if (typeDiff && typeDiff.length > 0) {
        diffTree.push({
          selectable: false, children: typeDiff, expanded: true,
          data: {
            propPath: propPath + 'type',
            fieldId,
            propCode: 'type',
            propName: SchemaPropName.getName('type')
          }
        });
      }
    }

    // 编辑器属性整体更新
    if (oldValue.editor && newValue.editor) {
      const editorDiff = this.contrastBasicProp(oldValue.editor, newValue.editor, fieldId, propPath + 'editor.', false);
      if (editorDiff && editorDiff.length > 0) {
        const data = {
          propPath: propPath + 'editor',
          fieldId,
          propCode: 'editor',
          propName: SchemaPropName.getName('editor'),
          oldValue: oldValue.editor,
          newValue: newValue.editor
        };
        diffTree.push({ selectable: true, data, isObject: true });
        this.changeList.push(data);
      }
    }



    let leftTree;

    // 字段本身变化
    if (diffTree.length > 0 || childLeftTree.length > 0) {
      this.changeContrast[oldValue.id] = diffTree;
      this.changeSelected[oldValue.id] = {};
      leftTree = { data: { ...oldValue } };
      this.selectedAll[oldValue.id] = false;
      if (childLeftTree.length > 0) {
        leftTree['children'] = childLeftTree;
        leftTree['expanded'] = true;
      }
    }

    if (deletedFields.length > 0) {
      deletedFields = [{ data: { ...oldValue }, children: deletedFields, selectable: false, expanded: true }];
    }

    return { leftTree, deletedFields };
  }

  private contrastBasicProp(oldValue, newValue, fieldId, path, selectable = true): any[] {
    const diffTree = [];
    Object.keys(oldValue).forEach(propCode => {
      if (typeof (oldValue[propCode]) !== 'object' && typeof (oldValue[propCode]) !== 'undefined') {
        // tslint:disable-next-line:triple-equals (为了适配默认值属性int和string之间的差异，故不采用!==)
        if (oldValue[propCode] != newValue[propCode]) {
          const data = {
            propPath: path + propCode, fieldId, propCode, propName: SchemaPropName.getName(propCode),
            oldValue: oldValue[propCode], newValue: newValue[propCode] === undefined ? '无' : newValue[propCode]
          };
          diffTree.push({ selectable, data });
          this.changeList.push(data);
        }
      }
    });

    return diffTree;
  }


  private handleChangeSet(oldSchemaEntities: FormSchemaEntity[], newSchemaEntities: FormSchemaEntity[]) {
    // 删除表
    if (this.deletedTable.length > 0) {
      oldSchemaEntities = oldSchemaEntities.filter(e => !this.deleteSelected[e.id]);
    }

    oldSchemaEntities.forEach(entity => {
      const newEntity = newSchemaEntities.find(e => e.id === entity.id);
      if (!newEntity) {
        return;
      }

      entity.type.fields = this.handleChangesetField(entity.type.fields);
      // 根节点下新增字段
      if (this.addedFields[entity.id] && this.addedFields[entity.id].length > 0) {
        this.addedFields[entity.id].forEach(field => {
          if (this.addSelected[field.id]) {
            entity.type.fields.push(field);
          }
        });
      }
      // 处理子表
      if (entity.type.entities && entity.type.entities.length > 0) {
        entity.type.entities = this.handleChangeSet(entity.type.entities, newEntity.type.entities);
      }

      // 新增子表
      if (this.addedTable.length > 0) {
        const addedTs = this.addedTable.filter(t => t.parentEntityId === entity.id);
        if (addedTs && addedTs.length) {
          addedTs.forEach(a => {
            if (this.addSelected[a.entity.id]) {
              entity.type.entities.push(a.entity);
            }
          });
        }
      }
    });
    return oldSchemaEntities;

  }

  private handleChangesetField(oldFields: FormSchemaEntityField[]): any[] {
    oldFields = oldFields.filter(oldField => {
      // 已删除字段
      if (this.deleteSelected[oldField.id]) {
        return false;
      }
      return true;
    });
    oldFields.forEach(oldField => {
      // 获取勾选的变更集
      const changeSelected = this.changeSelected[oldField.id], propPathSet = [];
      if (changeSelected) {
        Object.keys(changeSelected).forEach(key => {
          if (changeSelected[key]) {
            propPathSet.push(key);
          }
        });
        // 根据propPath 更新字段属性值（若path中的路径不存在，则会创建相应属性并赋值；若新值为undefined，则会删除相应属性）
        propPathSet.forEach(path => {
          const newValue = this.changeList.find(c => c.fieldId === oldField.id && c.propPath === path);
          // 为适配页面显示，将不存在的属性写成了无，故此处再改回undefined
          newValue.newValue = newValue.newValue === '无' ? undefined : newValue.newValue;
          set(oldField, path, newValue.newValue);
        });
      }


      // 下级字段
      if (oldField.type.fields) {
        oldField.type.fields = this.handleChangesetField(oldField.type.fields);
      }

      // 新增下级字段
      const addedDownFields = this.addedFields[oldField.id];
      if (addedDownFields && addedDownFields.length > 0) {
        addedDownFields.forEach(field => {
          if (this.addSelected[field.id]) {
            oldField.type.fields.push(field);
          }
        });
      }
    });

    return oldFields;

  }

  /**
   * 格式化json对象
   * @param object
   */
  showPropObject(object) {
    if (!object || typeof (object) !== 'object') {
      return object;
    }
    const objStr = JSON.stringify(object, null, 2);

    return objStr;
  }

  /**
   * 检查是否需要更新，若没有选中字段则不提示“更新成功”
   */
  private checkIsNeedUpdate(): boolean {
    const deSelected = Object.keys(this.deleteSelected).filter(d => this.deleteSelected[d]);
    if (deSelected.length > 0) {
      return true;
    }
    const addSelected = Object.keys(this.addSelected).filter(d => this.addSelected[d]);
    if (addSelected.length > 0) {
      return true;
    }

    const changeSelected = Object.keys(this.changeSelected).filter(d => {
      const c = Object.keys(this.changeSelected[d]).filter(s => this.changeSelected[d][s]);
      if (c.length > 0) {
        return true;
      }
      return false;
    });
    if (changeSelected.length > 0) {
      return true;
    }

    if (this.isUpdateVariable) {
      return true;
    }

    return false;
  }

  /**
   * 更新Dg ViewModel 和 DOM结构
   */
  private syncForm() {
    // 1、收集字段变更集：fieldId: { propPath, newValue}, 平铺type和editor的改动
    const changes = {};
    Object.keys(this.changeSelected).forEach(fieldId => {
      const selectedChange = this.changeSelected[fieldId], fieldChanges = [];
      Object.keys(selectedChange).forEach(propPath => {
        if (selectedChange[propPath]) {
          const changeInfo = this.changeList.find(c => c.fieldId === fieldId && c.propPath === propPath);
          if ((propPath === 'editor' || propPath === 'type') && changeInfo.newValue) {
            Object.keys(changeInfo.newValue).forEach(key => {
              fieldChanges.push({ propPath: propPath + '.' + key, newValue: changeInfo.newValue[key] });
            });
          } else {
            fieldChanges.push({
              propPath,
              newValue: changeInfo.newValue
            });
          }
        }
      });
      if (fieldChanges.length > 0) {
        changes[fieldId] = fieldChanges;
      }
    });

    // 2、重新组装 DgVM
    this.dgVMService.assembleDesignViewModel();

    // 3、 调用设计器定义的同步控件服务
    if (this.refreshAfterChangedServ) {
      this.refreshAfterChangedServ.refreshAfterSchemaChange(changes);
    }

  }

  /**
   * 针对标签页变更/切换树节点等场景中popover无法消失的问题，手动调用hide方法
   */
  hidePopoverContainers() {
    if (this.popoverContainers && this.popoverContainers.length) {
      this.popoverContainers.forEach(pop => {
        pop.hide();
      });
    }

  }

  /**
   * 新增tab- 全选点击事件
   */
  clickAddFieldSelectAllCheckbox() {

    Object.keys(this.addSelected).forEach(id => {
      this.addSelected[id] = this.addFieldSelectedAll;
    });
    this.cd.detectChanges();

  }
  /**
   * 删除tab- 全选点击事件
   */
  clickDeleteFieldSelectAllCheckbox() {
    Object.keys(this.deleteSelected).forEach(id => {
      this.deleteSelected[id] = this.deleteFieldSelectedAll;
    });
    this.cd.detectChanges();

  }
  /**
   * 新增tab- checkbox点击事件
   */
  clickAddSelectCheckbox(checked: boolean) {
    if (!checked) {
      this.addFieldSelectedAll = false;
    }

  }

  /**
   * 删除tab- checkbox点击事件
   */
  clickDeleteSelectCheckbox(checked: boolean) {
    if (!checked) {
      this.deleteFieldSelectedAll = false;
    }
  }


  compareJsonByString(json1: any, json2: any): boolean {
    const sortedJson1 = this.sortJsonByKey(json1);
    const sortedJson2 = this.sortJsonByKey(json2);
    return JSON.stringify(sortedJson1) === JSON.stringify(sortedJson2);
  }
  sortJsonByKey(jsonData) {
    const newkey = Object.keys(jsonData).sort();
    const newObj = {};
    // tslint:disable-next-line:prefer-for-of
    for (const key of newkey) {
      newObj[key] = jsonData[key];
    }
    return newObj;
  }
}
